package cn.bitkit.assist.code;

import cn.bitkit.assist.code.creator.*;
import cn.bitkit.assist.code.vo.*;
import com.alibaba.fastjson2.JSON;
import cn.bitkit.assist.code.creator.*;
import cn.bitkit.assist.code.vo.*;
import cn.bitkit.assist.doc.JavaDocHelper;
import cn.bitkit.base.annotation.Dictionary;
import cn.bitkit.base.annotation.EntityInfo;
import cn.bitkit.base.util.PathUtil;
import cn.bitkit.base.util.ReflectUtil;
import cn.bitkit.base.util.StringUtil;
import cn.bitkit.base.annotation.FieldInfo;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author changbo
 */
@Slf4j
public class CodeGenerator {

	public static void execute(CodeVO codeVO){
		execute(ProjectVO.init(), codeVO, OutputVO.toFile());
	}

	public static void execute(ProjectVO projectVO, CodeVO codeVO, OutputVO outputVO){
		try {
			EntityVO entityVO;
			if(codeVO.getEntityClass() != null){
				log.info("execute class:{}", codeVO.getEntityClass());
				initCodeVO(projectVO, codeVO);
				codeVO.setCreateEntity(false);
				codeVO.setCreateSql(true);
				entityVO = initEntityVOByClass(codeVO);
			}else{
				log.info("execute table:{}", codeVO.getTable());
				codeVO.setCreateEntity(true);
				codeVO.setCreateSql(false);
				entityVO = initEntityVOByTable(codeVO);
			}
			initOutputVO(projectVO, codeVO, outputVO);
			System.out.println(JSON.toJSONString(entityVO));
			create(EntityCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateEntity());
			create(MapperCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateMapper());
			create(DtoCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateDTO());
			create(ServiceCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateService());
			create(ControllerCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateController());
			create(SqlCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateSql());
			create(VueCreator.class, projectVO, codeVO, outputVO, entityVO, codeVO.getCreateWeb());
		} catch (Exception e) {
			log.error("", e);
		}
	}
	
	private static void create(Class<? extends BaseCreator> clazz, ProjectVO projectVO, CodeVO codeVO, OutputVO outputVO, EntityVO entityVO, boolean create) {
		if(!create) {
			return;
		}
		BaseCreator baseCreator = ReflectUtil.newInstance(clazz);
		baseCreator.setCodeVO(codeVO);
		baseCreator.setOutputVO(outputVO);
		baseCreator.setEntityVO(entityVO);
		baseCreator.setProjectVO(projectVO);
		baseCreator.create();
	}

	public static void initCodeVO(ProjectVO projectVO, CodeVO codeVO){
		String className = codeVO.getEntityClass().getName();
		String pkg = className.substring(0, className.lastIndexOf("."));
		String model = pkg.substring(pkg.lastIndexOf(".")+1);
		projectVO.setModuleName(model);
		pkg = pkg.substring(0, pkg.lastIndexOf("."));
		pkg = pkg.substring(0, pkg.lastIndexOf("."));
		pkg = pkg.substring(0, pkg.lastIndexOf("."));
		projectVO.setRootPkg(pkg);
	}

	public static EntityVO initEntityVOByTable(CodeVO codeVO){
		EntityVO vo = EntityVO.builder().build();

		return vo;
	}

	public static EntityVO initEntityVOByClass(CodeVO codeVO){
		EntityVO vo = EntityVO.builder().build();
		vo.setEntityClass(codeVO.getEntityClass());
		vo.setEntityName(codeVO.getEntityClass().getSimpleName());
		vo.setEntityVar(StringUtil.lowerFirst(codeVO.getEntityClass().getSimpleName()));
		vo.setTableName(StringUtil.camelToUnderscore(vo.getEntityName()));
		EntityInfo entityInfo = codeVO.getEntityClass().getAnnotation(EntityInfo.class);
		if(entityInfo != null && StringUtil.hasValue(entityInfo.name())){
			vo.setEntityComment(entityInfo.name());
		}
		if(entityInfo != null && StringUtil.hasValue(entityInfo.table())){
			vo.setTableName(entityInfo.table());
		}
		JavaDocHelper javaDocHelper = null;
		if(StringUtil.hasValue(codeVO.getEntityFile())){
			javaDocHelper = JavaDocHelper.of(codeVO.getEntityFile());
		}
		List<Field> fieldList = ReflectUtil.getFields(codeVO.getEntityClass());
		List<FieldVO> fieldVOList = new ArrayList<>();
		for(Field field : fieldList){
			FieldVO fieldVO = new FieldVO();
			fieldVO.setField(field.getName());
			fieldVO.setFieldClass(field.getType());
			fieldVO.setFieldUpper(StringUtil.upperFirst(field.getName()));
			fieldVO.setColumn(StringUtil.camelToUnderscore(field.getName()));
			fieldVO.setName(field.getName());
			initByPersistence(fieldVO, field);
			initByApiDoc(fieldVO, field);
			initByApiJavaDoc(javaDocHelper, fieldVO);
			boolean ok = initByFieldTag(fieldVO, field);
			if(!ok){
				continue;
			}
			initInput(fieldVO, field);
			fieldVOList.add(fieldVO);
		}
		vo.setFieldList(fieldVOList);
		FieldVO pk = fieldVOList.stream().filter(FieldVO::getId).findFirst().orElse(fieldVOList.getFirst());
		codeVO.setPrimaryKey(pk.getColumn());
		return vo;
	}

	private static void initByPersistence(FieldVO fieldVO, Field field){
		Id id = field.getAnnotation(Id.class);
		if(id != null){
			fieldVO.setId(true);
		}
		Column column = field.getAnnotation(Column.class);
		if(column != null && StringUtil.hasValue(column.name())){
			fieldVO.setColumn(column.name());
		}else{
			fieldVO.setColumn(StringUtil.camelToUnderscore(field.getName()));
		}
	}

	private static boolean initByFieldTag(FieldVO fieldVO, Field field){
		FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);
		if(fieldInfo == null) {
			fieldVO.setColumn(StringUtil.camelToUnderscore(field.getName()));
			return true;
		}
		if(fieldInfo.ignore()) {
			return false;
		}
		if(StringUtil.hasValue(fieldInfo.column())){
			fieldVO.setColumn(fieldInfo.column());
		}else{
			fieldVO.setColumn(StringUtil.camelToUnderscore(field.getName()));
		}
		if(fieldInfo.id()){
			fieldVO.setId(true);
		}
		if(StringUtil.hasValue(fieldInfo.value())){
			fieldVO.setName(fieldInfo.value());
		}
		return true;
	}

	private static void initByApiDoc(FieldVO fieldVO, Field field){
		ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
		if(apiModelProperty != null) {
			fieldVO.setName(apiModelProperty.value());
		}
		Schema schema = field.getAnnotation(Schema.class);
		if(schema != null) {
			fieldVO.setName(schema.name());
		}
	}

	private static void initByApiJavaDoc(JavaDocHelper javaDocHelper, FieldVO fieldVO){
		if(javaDocHelper == null){
			return;
		}
		if(StringUtil.hasValue(fieldVO.getName()) && !StringUtil.isEqual(fieldVO.getName(), fieldVO.getField())){
			return;
		}
		String name = javaDocHelper.getFieldDoc(fieldVO.getField());
		if(StringUtil.hasValue(name)){
			fieldVO.setName(name);
		}
	}

	private static void initInput(FieldVO fieldVO, Field field){
		fieldVO.setInput(true);
		if(fieldVO.getId()
				|| "create_time".equals(fieldVO.getColumn())
				|| "update_time".equals(fieldVO.getColumn())){
			fieldVO.setInput(false);
		}
		Dictionary dictionary = field.getAnnotation(Dictionary.class);
		if(dictionary != null){
			fieldVO.setDictInput(true);
			fieldVO.setDictClass(dictionary.value());
		}else{
			fieldVO.setDictInput(false);
		}
	}

	public static void initOutputVO(ProjectVO projectVO, CodeVO codeVO, OutputVO outputVO){
		//文件路径
		projectVO.setAbsoluteJavaFolder(PathUtil.asPath(projectVO.getProjectPath(), projectVO.getJavaBasePath(), projectVO.getRootPkg().replaceAll("\\.", "/")));
		projectVO.setAbsoluteJavaFolder(PathUtil.asPath(projectVO.getAbsoluteJavaFolder()));
		projectVO.setAbsoluteWebFolder(PathUtil.asPath(projectVO.getWebProjectPath()));
		projectVO.setAbsoluteSqlFolder(PathUtil.asPath(projectVO.getProjectPath(), projectVO.getSqlBasePath()));
		projectVO.setAbsoluteSqlFolder(PathUtil.asPath(projectVO.getAbsoluteSqlFolder()));
	}



}
